use std::mem::size_of;

use vulkan_rhi::{VulkanRHI, Vertex, PerObjectUniformData, Texture};

use crate::{vulkan::vulkan_rhi, utils::constant::MAX_FRAMES_IN_FLIGHT};

use super::object::{DrawCommandData, Drawable, Updateable, Object, UniformData, Componentable, UpdateableContext, ComponentableContext};

pub struct Square{
    object_render_data: DrawCommandData,
    uniform_data: UniformData,
    texture: Option<Texture>
}

impl Square {
    pub fn default(vulkan_rhi: &VulkanRHI) -> Result<Self, Box<dyn std::error::Error>> {
        Self::from_model_mat(vulkan_rhi, cgmath::Matrix4::from_scale(1.0))
    }
    
    pub fn from_model_mat(vulkan_rhi: &VulkanRHI, model_matrix: cgmath::Matrix4<f32>) -> Result<Self, Box<dyn std::error::Error>> {
        println!("[Object] New Square");
        
        // Constant Property
        let vertex_constant = vec![
            Vertex{position: [-0.5, -0.5, 0.0], color: [1.0, 0.0, 0.0], uv: [1.0, 0.0], ..Default::default()}, 
            Vertex{position: [ 0.5, -0.5, 0.0], color: [0.0, 1.0, 0.0], uv: [0.0, 0.0], ..Default::default()}, 
            Vertex{position: [ 0.5,  0.5, 0.0], color: [0.0, 0.0, 1.0], uv: [0.0, 1.0], ..Default::default()}, 
            Vertex{position: [-0.5,  0.5, 0.0], color: [1.0, 1.0, 1.0], uv: [1.0, 1.0], ..Default::default()}
        ];
        let index_constant: Vec<u32> = vec![
            0, 1, 2, 2, 3, 0
        ];

        // Setup
        let index_len = index_constant.len() as u32;
        let texture = Texture::load(&vulkan_rhi, String::from("./vulkan-tutorial-rs/assets/texture.jpg"))?;//.expect("[Error] Failed to Load Texture");

        let vertices = vulkan_rhi
            .create_buffer_usage_v2((vertex_constant.len() * size_of::<Vertex>()) as u64, ash::vk::BufferUsageFlags::VERTEX_BUFFER, Some(vertex_constant));
        let indices = vulkan_rhi
            .create_buffer_usage_v2((index_constant.len() * size_of::<u32>()) as u64, ash::vk::BufferUsageFlags::INDEX_BUFFER, Some(index_constant));
        let uniform_buffer = vulkan_rhi
            .create_uniform_buffers_v2(MAX_FRAMES_IN_FLIGHT, size_of::<PerObjectUniformData>() as u64);
        let descriptor_sets = vulkan_rhi.create_descriptor_set_v2(
                &uniform_buffer.buffer, &texture.sampler, &texture.image_view, None, 0);

        // Return
        Ok(Self {
            object_render_data: DrawCommandData {
                vertices: Some(vertices),
                indices: Some(indices),
                descriptor_sets: Some(descriptor_sets),
                index_len: index_len
            },
            uniform_data: UniformData {
                gpu_buffer: Some(uniform_buffer),
                model_matrix: model_matrix,
                is_animated: 0.0,
            },
            texture: Some(texture),
        })
    }
}

impl Drawable for Square {
    fn draw(&self, vulkan_rhi: &VulkanRHI, command_buffer_index: usize) {
        self.object_render_data.draw(vulkan_rhi, command_buffer_index);
    }
}
impl Updateable for Square {
    fn update(&mut self, context: &UpdateableContext) {
        self.uniform_data.model_matrix = 
            cgmath::Matrix4::from_angle_y(cgmath::Rad(context.delta_time)) * self.uniform_data.model_matrix;

        self.uniform_data.update(context);
    }
}
impl Componentable for Square {
    fn tick(&mut self, _delta_time: f32, _context: &ComponentableContext) {
        
    }
}
impl Object for Square {
    fn release_resource(&mut self, vulkan_rhi: &VulkanRHI) {
        self.uniform_data.release_resource(vulkan_rhi);
        self.object_render_data.release_resource(vulkan_rhi);

        if let Some(texture) = &self.texture { unsafe {
            vulkan_rhi.logic_device.destroy_sampler(texture.sampler, None);
            vulkan_rhi.logic_device.destroy_image_view(texture.image_view, None);
            vulkan_rhi.logic_device.destroy_image(texture.image, None);
            vulkan_rhi.logic_device.free_memory(texture.device_memory, None);   
        }}
    }
}